﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Generator;

namespace ALDIOrganizer
{
    /* The actual generating takes place here.
     * All the relevant tools are provided by Engine + associated classes,
     * but this form is responsible for making use of them in proper manner and order. */
    public partial class GeneratingRota : Form
    {
        private Engine engine;        

        private Rota rota;
        private Template template;
        private Parameters _Parameters;

        public bool[] impossibleDays; 
                

        private BackgroundWorker worker1;
        private BackgroundWorker worker2;
        private BackgroundWorker worker3;
        private delegate void reportProgressDelegate(byte percent, string message, string title);
        private reportProgressDelegate updateProgress;
                

        public GeneratingRota()
        {
            InitializeComponent();
         
        }

        public GeneratingRota(Rota rota,
            Template template,
            Parameters parameters) : this()
        {
            this.rota = rota;
            this.template = template;
            this._Parameters = parameters;
            this.engine = new Engine(_Parameters);            
        }
        
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            updateProgress = new reportProgressDelegate(UpdateProgressBar);

            string title = String.Empty;
            string message = String.Empty;
            if (e.UserState is string[])
            {                
                    var strings = (string[])e.UserState;
                    title = strings[0];
                    if(strings.Length > 1)
                        message = strings[1];                                
            }
            updateProgress((byte)e.ProgressPercentage, title, message);            
        }        

        private void UpdateProgressBar(byte percent, string message, string title)
        {
            pbTesting.Value = percent;
            if (message != String.Empty)
            {
                if (lbDetails.Text != message)
                {
                    lbDetails.Text = message;
                }
            }

            if (title != String.Empty)
            {
                if (lbWhatDoing.Text != title)
                {
                    lbWhatDoing.Text = title;
                }
            }
        }
        

        private void CheckImpossibilities(object sender, DoWorkEventArgs e)
        {
            // lbWhatDoing.Text = "Testing for impossibilities...";

            
            impossibleDays = DetectImpossibilities.DayByDayTest(rota, template, worker1);
            if(impossibleDays[7]) e.Cancel=true;
        }

        private void NoImpossibilitiesFound(object sender, RunWorkerCompletedEventArgs e)
        {
            
            if (e.Cancelled)
            {
                this.Close();
                return;
            }                        
            worker2.DoWork += BuildBasicSolution;
                        
            worker2.RunWorkerCompleted += BasicSolutionCreated;
            
            worker2.RunWorkerAsync();
        }        

        private void BuildBasicSolution(object sender, DoWorkEventArgs e)
        {
            var result = engine.Go(engine.solution, -1, -1, false, worker2);
            if (result == -1)
            {
                e.Cancel = true;
            }
            if (result == -2)
            {
                e.Cancel = true;
            }         
        }

        private void BasicSolutionCreated(object sender, RunWorkerCompletedEventArgs e)
        {            
            if (e.Cancelled)
            {
                Close();
                return;
            }
            worker3.DoWork += OptimizeSolution;
            worker3.RunWorkerCompleted += OptimizationFinished;            
            worker3.RunWorkerAsync();
        }

        private void OptimizeSolution(object sender, DoWorkEventArgs e)
        {
            // lbWhatDoing.Text = "Optimizing the solution...";   
            

            GeneticOptimizer.SetInitialSolution(engine.solutionFinal);
            GeneticOptimizer.PassMeBackgroundWorker(worker3);
            GeneticOptimizer.startTheGame(_Parameters.Speed); // do przywrócenia!!


            if (worker3.CancellationPending)
            {
                e.Cancel = true;
            }

            
        }

        private void OptimizationFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                Close();
                return;
            }
            
            rota.ClearAllShifts();
            engine.FillRotaWithFinalSolution(GeneticOptimizer.Champion);
            
            Sounds.Play(Sounds.Arpeggio);

            DialogResult = DialogResult.OK;
            this.Close();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (worker1.IsBusy) worker1.CancelAsync();
            if (worker2.IsBusy) worker2.CancelAsync();
            if (worker3.IsBusy) worker3.CancelAsync();
        }        

        private void formGenerator_Load(object sender, EventArgs e)
        {
            worker1 = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};
            worker2 = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};
            worker3 = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};

            Penalties.PreparePenaltyCharts();
            engine.ImportData(rota, template, Time.AllWeekdays);

            worker1.ProgressChanged += worker_ProgressChanged;
            worker2.ProgressChanged += worker_ProgressChanged;
            worker3.ProgressChanged += worker_ProgressChanged;
            worker1.DoWork += CheckImpossibilities;
            worker1.RunWorkerCompleted += NoImpossibilitiesFound;
            
            

            Show();
            
            worker1.RunWorkerAsync();

        }

        private void lbDetails_TextChanged(object sender, EventArgs e)
        {
            lbDetails.Left = (Program.generatorForm.Width - lbDetails.Width) / 2;
        }

        
    }
}
